package ru.galiauir.main;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.Scanner;

import ru.galiauir.engine.Simulator;
import ru.galiauir.gui.MainForm;
import ru.galiauir.utils.IOUtils;
import ru.galiauir.utils.PropertyFileReader;


public class Runner {
	public static final String NO_LOGGED_OUTPUT_DIR_UNIX = "./imitator_output";
	public static final String NO_LOGGED_OUTPUT_DIR_WIN = "H:\\imitator_output";
	public static final String MONTHLY_DATA_FILE = "monthly.txt";
	public static final String INVITATION_DATA_FILE = "invitation.txt";
	public static final String PARAMS_FILE = "params.sql";

	private static final int MAX_DAYS = 3650;

	private static final int MAX_INIT_USERS = 1000;

	private static final String usage = "java -jar [extra jvm arguments] imitator_0.0.1.jar alpha number_of_days initial_number_of_users beta averageAmount dispersia \"y Ratio\" [-cm]";

	private static final String INSERT_PRM_STRING =
			"INSERT INTO RLISTPRM (listprmid, typeprmid, listprmname, listprmdescr, listprmvalue, listprmstepvalue, listprmstepcount) " +
			"VALUES	({0}, {1}, {2}, {3}, {4}, {5}, {6})";	
	
	@SuppressWarnings("unused")
	private static MainForm userForm;

	private static boolean isConsoleMode = false;
	private static boolean isFileMode = false;
	private static boolean isCycleMode = false;

	public static void main(String args[]) {
		Float alpha;
		int maxDays;
		int initUsers;
		Float beta;
		Integer yRatio;
		long averageAmount, dispersia;
		Float basicCost;

		int fileParamIndex = 0;
		if (args != null && args.length > 0) {
			for (String arg : args) {
				if (arg.equals("-file")) {
					isFileMode = true;
					fileParamIndex++;
					break;
				}
				fileParamIndex++;
			}
		}

		if (args != null && args.length > 0) {
			for (String arg : args) {
				if (arg.equals("-cm")) {
					isConsoleMode = true;
				} else if (arg.equals("-cycle")) {
					isCycleMode = true;
				}
			}
		}

		if (isCycleMode) {
			alpha = 0.001f;
			maxDays = 720;
			initUsers = 1000;
			beta = 0.03f;
			yRatio = 4;
			averageAmount = 2500;
			dispersia = 557;
			basicCost = 0.75f;

			final float START_ALPHA = 0.001f;
			final float ALPHA_STEP = 0.005f;
			final float MAX_ALPHA = 0.05f;

			final int START_Y = 5;
			final int Y_STEP = 5;
			final int MAX_Y = 100;

			int cycle = 0;
			int cycleNum = ((int) ((MAX_ALPHA - START_ALPHA) / ALPHA_STEP) + 1) * ((int) ((MAX_Y - START_Y) / Y_STEP) + 1);

			String dir = System.getProperty("os.name").toLowerCase().contains("win") ? NO_LOGGED_OUTPUT_DIR_WIN : NO_LOGGED_OUTPUT_DIR_UNIX; 
			
			File paramsFile = new File(dir + "/" + PARAMS_FILE);
			paramsFile.delete();

			OutputStream paramOs = null;
			try {
				paramOs = new BufferedOutputStream(new FileOutputStream(paramsFile), 1024);
				PrintWriter pw = new PrintWriter(paramOs);
				pw.println(MessageFormat.format(INSERT_PRM_STRING, 1, 2, "'alpha'", "'alpha'", Float.valueOf(START_ALPHA).toString(), Float.valueOf(ALPHA_STEP).toString(), (int) ((MAX_ALPHA - START_ALPHA) / ALPHA_STEP) + 1));
				pw.println(MessageFormat.format(INSERT_PRM_STRING, 2, 2, "'y_ratio'", "'y_ratio'", START_Y, Y_STEP, (int) ((MAX_Y - START_Y) / Y_STEP) + 1));
				pw.flush();
				paramOs.flush();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				IOUtils.closeSilent(paramOs);
			}


			File monthlyDataFile = new File(dir + "/" + MONTHLY_DATA_FILE);
			monthlyDataFile.delete();

			File inviteFile = new File(dir + "/" + INVITATION_DATA_FILE);
			inviteFile.delete();

			OutputStream monthlyDataOs = null;
			OutputStream inviteOs = null;
			try {
				monthlyDataOs = new BufferedOutputStream(new FileOutputStream(monthlyDataFile), 1024 * 4);
				inviteOs = new BufferedOutputStream(new FileOutputStream(inviteFile), 1024);

				for (alpha = START_ALPHA; alpha <= MAX_ALPHA; alpha += ALPHA_STEP) {
					for (yRatio = START_Y; yRatio <= MAX_Y; yRatio += Y_STEP) {
						System.out.println(cycle + " of " + cycleNum);
						Simulator.simulate(alpha, maxDays, initUsers, beta, averageAmount, dispersia, yRatio, basicCost, true, true, true, monthlyDataOs, inviteOs);
						cycle++;
						monthlyDataOs.flush();
						inviteOs.flush();
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				IOUtils.closeSilent(monthlyDataOs);
				IOUtils.closeSilent(inviteOs);
			}
		} else if (isFileMode) {
			try {
				PropertyFileReader.getInstance().readPropsFromFile(args[fileParamIndex]);
				alpha = PropertyFileReader.getInstance().getFloatProperty(PropertyFileReader.ALPHA, 0.0f, 1.0f);
				maxDays = PropertyFileReader.getInstance().getIntProperty(PropertyFileReader.MAX_DAYS, 1, MAX_DAYS);
				initUsers = PropertyFileReader.getInstance().getIntProperty(PropertyFileReader.INIT_USERS, 1, MAX_INIT_USERS);
				beta = PropertyFileReader.getInstance().getFloatProperty(PropertyFileReader.BETA, 0.0f, 1.0f);
				yRatio = PropertyFileReader.getInstance().getIntProperty(PropertyFileReader.Y_RATIO, null, null);
				averageAmount = PropertyFileReader.getInstance().getLongProperty(PropertyFileReader.AVERAGE_AMOUNT, 0l, null);
				dispersia = PropertyFileReader.getInstance().getLongProperty(PropertyFileReader.DISPERSIA, 0l, null);
				basicCost = PropertyFileReader.getInstance().getFloatProperty(PropertyFileReader.BASIC_COST, 0.01f, 0.99f);
				Simulator.simulate(alpha, maxDays, initUsers, beta, averageAmount, dispersia, yRatio, basicCost, true, true, false, null, null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else if (isConsoleMode && args != null && args.length == 6) {
			try {
				alpha = Float.parseFloat(args[0]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[0], usage));
				System.exit(1);
				return;
			}

			if (alpha <= 0 || alpha >= 1) {
				System.out.println(String.format("Invalid alpha. Required float: (%d, %d)", 0.0f, 1.0f));
			}

			try {
				maxDays = Integer.parseInt(args[1]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[1], usage));
				System.exit(2);
				return;
			}

			if (maxDays < 1 || maxDays > MAX_DAYS) {
				System.out.println(String.format("Too many days. That would last forever. %d is max value", MAX_DAYS));
				System.exit(3);
				return;
			}

			try {
				initUsers = Integer.parseInt(args[2]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[2], usage));
				System.exit(2);
				return;
			}

			if (initUsers < 1 || initUsers > MAX_INIT_USERS) {
				System.out.println(String.format("Too many initial users. %d is max value", MAX_INIT_USERS));
				System.exit(3);
				return;
			}

			try {
				beta = Float.parseFloat(args[3]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[3], usage));
				System.exit(2);
				return;
			}

			if (beta < 0.0f || beta > 1.0f) {
				System.out.println(String.format("Invalid beta. 0.0 < beta < 1.0"));
				System.exit(3);
				return;
			}

			try {
				averageAmount = Long.parseLong(args[4]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[4], usage));
				System.exit(2);
				return;
			}

			if (averageAmount <= 0l) {
				System.out.println(String.format("Invalid averageAmount."));
				System.exit(3);
				return;
			}

			try {
				dispersia = Long.parseLong(args[5]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[5], usage));
				System.exit(2);
				return;
			}

			if (dispersia <= 0l) {
				System.out.println(String.format("Invalid dispersia."));
				System.exit(3);
				return;
			}

			try {
				yRatio = Integer.parseInt(args[6]);
			} catch (Exception e) {
				System.out.println(String.format("Wrong call. %s is not a number\n\nUsage: %s", args[6], usage));
				System.exit(2);
				return;
			}

			if (dispersia <= 0l) {
				System.out.println(String.format("Invalid y Ratio."));
				System.exit(3);
				return;
			}
			Simulator.simulate(alpha, maxDays, initUsers, beta, averageAmount, dispersia, yRatio, 0.75f, isConsoleMode);//TODO: make parametrized
		} else if (isConsoleMode) {
			Scanner scanner = new Scanner(System.in);
			System.out.println("Enter alpha. 0.0 < alpha < 1.0");
			String in;
			try {
				in = scanner.nextLine();
				alpha = Float.parseFloat(in);
				if (alpha <= 0.0f || alpha >= 1.0) {
					System.out.println("Invalid value. Terminating...\n");
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter simulation days. Maximal value = %d", MAX_DAYS));
			try {
				in = scanner.nextLine();
				maxDays = Integer.parseInt(in);
				if (maxDays < 1 || maxDays > MAX_DAYS) {
					System.out.println(String.format("Invalid value. Maximal value: %d. Terminating...\n", MAX_DAYS));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter initial users number. Maximal value = %d", MAX_INIT_USERS));
			try {
				in = scanner.nextLine();
				initUsers = Integer.parseInt(in);
				if (initUsers < 1 || initUsers > MAX_INIT_USERS) {
					System.out.println(String.format("Invalid value. Maximal value: %d. Terminating...\n", MAX_INIT_USERS));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter beta. 0.0 < beta < 1.0"));
			try {
				in = scanner.nextLine();
				beta = Float.parseFloat(in);
				if (beta < 0.0f || beta > 1.0f) {
					System.out.println(String.format("Invalid beta. 0.0 < beta < 1.0"));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter averageAmount > 0"));
			try {
				in = scanner.nextLine();
				averageAmount = Long.parseLong(in);
				if (averageAmount <= 0l) {
					System.out.println(String.format("Invalid averageAmount."));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter dispersia > 0"));
			try {
				in = scanner.nextLine();
				dispersia = Long.parseLong(in);
				if (dispersia <= 0) {
					System.out.println(String.format("Invalid dispersia."));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			System.out.println(String.format("Enter y Ratio > 0"));
			try {
				in = scanner.nextLine();
				yRatio = Integer.parseInt(in);
				if (yRatio <= 0) {
					System.out.println(String.format("Invalid y Ratio."));
					System.exit(1);
					return;
				}
			} catch (NumberFormatException nfe) {
				System.out.println("Invalid value. Terminating...\n");
				System.exit(1);
				return;
			}

			Simulator.simulate(alpha, maxDays, initUsers, beta, averageAmount, dispersia, yRatio, 0.75f, isConsoleMode);//TODO: make parametrized
		} else {
			userForm = new MainForm();
		}

	}

}
